/**
 * @file ShadedMode.cpp
 * @see ShadedMode
 * @see IRenderMode
 * @author M. Caputo
 * @date 2010-06-11
 */

#include <QtOpenGL>
#include "ShadedMode.h"

void ShadedMode::setSettings()
{
	oldScene = 0;
	displayList = 0;
	
	glEnable(GL_DEPTH_TEST);	
	glEnable(GL_LIGHT0);
	glEnable(GL_LIGHT1);
	glEnable(GL_COLOR_MATERIAL);
	
	// because our normal vectors are not normalized yet...
	glEnable(GL_NORMALIZE);
}

void ShadedMode::unsetSettings()
{
	glDisable(GL_DEPTH_TEST);
	glDisable(GL_LIGHT0);
	glDisable(GL_LIGHT1);
	glDisable(GL_COLOR_MATERIAL);
	glDisable(GL_NORMALIZE);
}

/**
 * @brief Draws a flat or smooth shaded IScene object
 *
 * This is an implenentation of IRenderMode::draw() with an additional
 * parameter to control the shading. To use it, you can
 * inherit a new render mode from this class and delegate draw() to
 * this method!
 *
 * @param [in] scene The scene which should be drawn
 * @param [in] defaultColor The default scene color
 * @param [in] smooth Enable smooth shading
 */
void ShadedMode::drawIntern(const IScene *scene, const QColor & defaultColor, bool smooth)
{	
	// If the scene or the smooth value has changed, we need to create an new display list
	if (displayList == 0 || oldScene != scene || oldSmooth != smooth) {
		createDisplayList(scene, smooth);
		oldScene = scene;
		oldSmooth = smooth;
	}
	
	// The model is always white, the colors are from the light sources!
	glColor3f(1.0, 1.0, 1.0);
	
	// Specular settings
	const float specular[] = { 1.0, 1.0, 1.0, 1.0 };
	const float specularReflection[] = { 0.5, 0.5, 0.5, 1.0 };
	glMaterialfv(GL_FRONT, GL_SPECULAR, specularReflection);
	glMateriali(GL_FRONT, GL_SHININESS, 50);
	
	// light0 settings (main light)
	const float light0Position[4] = { 3.0, 4.0, 5.0, 0.0 };
	const float light0Color[] = { 
		defaultColor.redF(),
		defaultColor.greenF(),
		defaultColor.blueF(),
		1.0
	};
	glLightfv(GL_LIGHT0, GL_POSITION, light0Position);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, light0Color);
	glLightfv(GL_LIGHT0, GL_SPECULAR, specular);
	glEnable(GL_LIGHT0);
	
	// light1 settings (backup light, half as bright as light0)
	const float light1Position[4] = { -3.0, -2.0, -3.0, 0.0 };
	const float light1Color[] = { 
		defaultColor.redF() / 2.0,
		defaultColor.greenF() / 2.0,
		defaultColor.blueF() / 2.0,
		1.0
	};
	glLightfv(GL_LIGHT1, GL_POSITION, light1Position);
	glLightfv(GL_LIGHT1, GL_DIFFUSE, light1Color);
	glLightfv(GL_LIGHT1, GL_SPECULAR, specular);
	glEnable(GL_LIGHT1);
	
	// Turn on the light only for the object on
	glEnable(GL_LIGHTING);
	glCallList(displayList);
	glDisable(GL_LIGHTING);
}

/**
 * @brief Creates a display list for the object 
 *
 * To get more perfomance out of OpenGL we use the display list
 * feature. This can create a precompiled list of OpenGL commands
 * which can be drawn much faster than with the normal immediate mode.
 * The refenece number for the display list will be stored in the
 * object variable displayList.
 *
 * @see ShadedMode::displayList
 * @param [in] scene The scene which should be drawn
 * @param [in] smooth Enable smooth shading
 */
void ShadedMode::createDisplayList(const IScene *scene, bool smooth)
{
	displayList = glGenLists(1);
	glNewList(displayList, GL_COMPILE);

		int cp = scene->polygonsCount();
		for(int i=0; i<cp; i++) {
			const CPolygon *poly = scene->polygon(i);
			int cv = poly->verticesCount();
			const float *nv = 0;
			if (!smooth) {
				nv = poly->normalVector();
			}
			glBegin( GL_POLYGON );
				for(int j=0; j<cv; j++) {
					const CVertex *vert = poly->vertex(j);
					const float *data = vert->vertex();
					if (smooth) {
						nv = vert->normalVector();
					}
					glNormal3fv(nv);
					glVertex3fv(data); 
				}
			glEnd();
		}
	
	glEndList ();
}
